import re
import os
import requests
import time
import json
import mutagen  # 用于音频文件验证
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn, DownloadColumn, TransferSpeedColumn
from rich.panel import Panel
from rich.table import Table
from rich.prompt import Prompt
from rich.text import Text
from rich.status import Status
from rich.style import Style

# 初始化rich控制台
console = Console()

# 存储目录（手机可见路径）
music_dir = '网易云音乐下载/'
if not os.path.exists(music_dir):
    os.makedirs(music_dir)

# 第三方API（无需Cookie）
API_URL = "https://api.injahow.cn/meting/?server=netease&type=playlist&id="

def extract_playlist_id(share_url):
    """从分享链接中提取歌单ID"""
    if 'id=' in share_url:
        match = re.search(r'id=(\d+)', share_url)
        if match:
            return match.group(1)
    # 处理短链接情况
    try:
        with console.status("[bold green]处理短链接中...", spinner="dots"):
            response = requests.head(share_url, allow_redirects=True, timeout=5)
            return extract_playlist_id(response.url)
    except:
        return None

def format_file_size(size_bytes):
    """格式化文件大小显示"""
    for unit in ['B', 'KB', 'MB']:
        if size_bytes < 1024.0:
            return f"{size_bytes:.2f} {unit}"
        size_bytes /= 1024.0
    return f"{size_bytes:.2f} GB"

def is_valid_audio(file_path):
    """验证音频文件是否有效"""
    try:
        # 尝试加载音频文件
        audio = mutagen.File(file_path)
        # 检查文件大小和音频信息
        if audio and os.path.getsize(file_path) > 1024 * 10:  # 大于10KB
            return True
        return False
    except:
        return False

def main():
    # 显示欢迎界面
    welcome_text = Text()
    welcome_text.append("网易云音乐下载器\n", style=Style(color="cyan", bold=True))
    
    console.print(Panel(
        welcome_text,
        title="🎵 音乐下载工具",
        title_align="left",
        border_style="blue",
        padding=(1, 2)
    ))
    
    # 显示操作步骤
    steps = Table(show_header=False, box=None, padding=(0, 2))
    steps.add_row("[bold cyan]1.[/bold cyan]", "在网易云音乐APP中打开歌单")
    steps.add_row("[bold cyan]2.[/bold cyan]", "点击右上角分享，选择'复制链接'")
    steps.add_row("[bold cyan]3.[/bold cyan]", "将链接粘贴到下方")
    console.print(Panel(steps, title="操作步骤", border_style="magenta", padding=(0, 1)))
    console.print()
    
    # 获取分享链接
    share_url = Prompt.ask("[bold yellow]请粘贴歌单分享链接", default="")
    if not share_url:
        console.print("[bold red]❌ 链接不能为空[/bold red]")
        return
    
    # 提取歌单ID
    playlist_id = extract_playlist_id(share_url)
    if not playlist_id:
        console.print("[bold red]❌ 无法解析歌单ID，请检查链接是否正确[/bold red]")
        return
    
    try:
        # 获取歌单信息
        with Status("[bold blue]正在获取歌单信息...", spinner="dots", console=console):
            api_response = requests.get(f"{API_URL}{playlist_id}", timeout=10)
            songs = json.loads(api_response.text)
        
        if not songs:
            console.print("[bold yellow]⚠️ 未找到歌曲信息，可能是个空歌单[/bold yellow]")
            return
        
        console.print(f"[green]✅ 成功加载歌单，共发现 {len(songs)} 首歌曲[/green]\n")
        
        # 显示歌单信息预览
        preview = Table(title="歌曲预览", show_lines=True, border_style="dim")
        preview.add_column("序号", width=6)
        preview.add_column("歌曲名", width=30)
        preview.add_column("歌手", width=20)
        
        # 只显示前5首作为预览
        for i, song in enumerate(songs[:5], 1):
            preview.add_row(
                str(i),
                song.get('name', '未知歌曲'),
                ', '.join(song.get('artist', ['未知歌手']))
            )
        if len(songs) > 5:
            preview.add_row("...", "...", "...")
        
        console.print(preview)
        console.print()
        
        # 确认下载
        confirm = Prompt.ask(
            f"是否开始下载这 {len(songs)} 首歌曲",
            choices=["y", "n"],
            default="y"
        )
        
        if confirm.lower() != 'y':
            console.print("[yellow]⚠️ 已取消下载[/yellow]")
            return
        
        # 下载歌曲
        success_count = 0
        fail_count = 0
        exist_count = 0
        invalid_count = 0
        
        with Progress(
            SpinnerColumn(spinner_name="circle"),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(bar_width=None),
            TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
            TimeRemainingColumn(),
            DownloadColumn(),
            TransferSpeedColumn(),
            console=console,
            expand=True
        ) as progress:
            main_task = progress.add_task("[cyan]总体进度", total=len(songs))
            
            for i, song in enumerate(songs, 1):
                try:
                    # 提取歌曲信息
                    song_name = song.get('name', f'未知歌曲{i}')
                    artists = song.get('artist', ['未知歌手'])
                    artist_str = ', '.join(artists)
                    full_name = f"{song_name} - {artist_str}"
                    safe_name = re.sub(r'[\\/*?:"<>|]', '', full_name)
                    save_path = f"{music_dir}{safe_name}.mp3"
                    
                    # 检查文件是否已存在且有效
                    if os.path.exists(save_path):
                        if is_valid_audio(save_path):
                            exist_count += 1
                            console.print(f"[{i}/{len(songs)}] [yellow]⏭️ 已存在: {full_name}")
                            progress.update(main_task, advance=1)
                            continue
                        else:
                            console.print(f"[{i}/{len(songs)}] [orange]🔄 检测到无效文件，重新下载: {full_name}")
                            os.remove(save_path)  # 删除无效文件
                    
                    # 获取下载链接
                    download_url = song.get('url')
                    if not download_url:
                        fail_count += 1
                        console.print(f"[{i}/{len(songs)}] [red]❌ 无权限下载: {full_name}")
                        progress.update(main_task, advance=1)
                        continue
                    
                    # 创建单个文件下载任务
                    file_task = progress.add_task(f"[green]下载: {song_name}", total=None)
                    
                    # 下载歌曲
                    with requests.get(download_url, stream=True, timeout=30) as r:
                        r.raise_for_status()
                        total_size = int(r.headers.get('content-length', 0))
                        progress.update(file_task, total=total_size)
                        
                        with open(save_path, 'wb') as f:
                            downloaded = 0
                            for chunk in r.iter_content(chunk_size=8192):
                                if chunk:
                                    f.write(chunk)
                                    downloaded += len(chunk)
                                    progress.update(file_task, completed=downloaded)
                    
                    # 完成单个文件下载
                    progress.remove_task(file_task)
                    
                    # 验证下载的音频文件
                    if is_valid_audio(save_path):
                        success_count += 1
                        file_size = format_file_size(os.path.getsize(save_path))
                        console.print(f"[{i}/{len(songs)}] [green]✅ 下载成功: {full_name} ({file_size})")
                    else:
                        invalid_count += 1
                        os.remove(save_path)  # 删除无效文件
                        console.print(f"[{i}/{len(songs)}] [orange]❌ 下载的文件无效: {full_name}")
                    
                    progress.update(main_task, advance=1)
                    time.sleep(0.5)  # 控制频率
                    
                except Exception as e:
                    fail_count += 1
                    console.print(f"[{i}/{len(songs)}] [red]❌ 下载失败 {full_name}: {str(e)}")
                    progress.update(main_task, advance=1)
        
        # 显示下载统计
        console.print("\n")
        console.print(Panel.fit(
            "[bold]下载完成[/bold]",
            border_style="green",
            title="🎉 任务结束"
        ))
        
        stats = Table(title="下载统计", border_style="green", show_lines=True)
        stats.add_column("状态", justify="left", style="bold")
        stats.add_column("数量", justify="right")
        stats.add_column("占比", justify="right")
        
        total = len(songs)
        stats.add_row(
            "[green]成功下载",
            f"{success_count}",
            f"{(success_count/total*100):.1f}%"
        )
        stats.add_row(
            "[yellow]已存在(跳过)",
            f"{exist_count}",
            f"{(exist_count/total*100):.1f}%"
        )
        stats.add_row(
            "[orange]文件无效",
            f"{invalid_count}",
            f"{(invalid_count/total*100):.1f}%"
        )
        stats.add_row(
            "[red]下载失败",
            f"{fail_count}",
            f"{(fail_count/total*100):.1f}%"
        )
        stats.add_row(
            "[blue]总计",
            f"{total}",
            "100%"
        )
        
        console.print(stats)
        console.print(f"[blue]文件保存目录: {os.path.abspath(music_dir)}[/blue]")
        
        # 提供无法播放的解决方案
        if invalid_count > 0:
            console.print("\n[yellow]💡 提示: 部分文件无法播放，建议尝试:[/yellow]")
            console.print("1. 重新运行程序下载（可能是临时网络问题）")
            console.print("2. 确保播放器支持MP3格式")
            console.print("3. 尝试使用VLC等万能播放器")
    
    except Exception as e:
        console.print(f"[bold red]程序错误: {str(e)}[/bold red]")
        console.print("[yellow]提示：请检查网络连接或稍后重试（API可能暂时不可用）[/yellow]")

if __name__ == "__main__":
    try:
        # 检查并安装依赖
        import rich
        import mutagen
    except ImportError as e:
        missing = str(e).split("'")[1]
        console.print(f"[yellow]检测到缺少{missing}库，正在自动安装...[/yellow]")
        os.system(f"pip install {missing}")
    
    try:
        main()
    except KeyboardInterrupt:
        console.print("\n[yellow]⚠️ 程序已被用户中断[/yellow]")
    except Exception as e:
        console.print(f"[bold red]致命错误: {str(e)}[/bold red]")
    